import axios from "axios";

const URL_ENCODE = "https://www.amministrazionicomunali.net/imu/f24_2025.php";

const DEFAULT_HEADERS = ['zzaddeopsinmbugogoardreshorrhepppeserenmqqcyraessrsehekhpesehenmdqrrreccpevxhegcrfuurappqqarcvgcgtarcvgcgtarcvgcgtarcvgcgtarcvgcgtarcvgcgtadvhopcunmdpqqgcadfoqqcunmregcperrbekhcueerenmarvxhegcxvunreeearadvhopcuadcsgcperrbekhppcyracctheeregovsmubqiiii'
	,"zzaddeopsinmbugogoardreshorrhepppeserenmqqcyraessrsehekhpesehenmdqrrreccpevxhegcrfuurappqqarcvgcgtarcvgcgtarcvgcgtarcvgcgtarcvgcgtarcvgcgtadvhopcunmdpqqgcadfoqqcuadcsgcperrbekhcueerenmarvxhegcxvunreeearadvhopcuadcsgcperrbekhppcyracctheeregovsmubqiiii", 'zzaddeopsinmbugogoardreshorrhepppeserenmqqcyraessrsehekhpesehenmdqrrreccpevxhegcrfuurappqqarcvgcgtarcvgcgtarcvgcgtarcvgcgtarcvgcgtarcvgcgtadvhopcunmdpqqgcadfoqqcunmcvgcperrbekhcueerenmarvxhegcxvunreeearadvhopcuadcsgcperrbekhppcyracctheeregovsmubqiiii'];

const TO_DISCARD = [
	"itaddpnmgtdd",
	"vharfoadvharfoadvharfoadii",
	"ddcsgovhddcsgovhddcsgovh",
	"nmgtddcunmgtddcunmgtddgoiiii",
];

let differentHeaders = [];

export const POS0_TOKENS = [
	"go","gc","cu","si","qq","be","ke","cs","vs","rs","sr","ct","gt","kh","gn","hp"
];

export const MAIN_TOKENS = [ // gruppo 3 e 4
	"op","to","fo","de","cn","es","dr","cv","dd","gg","ss","uu","ad","nm","mu","ms",
	"bq","qa","dp","da","ma","cc","he","re","vx","ee","se","rr","cy","un","vt","ar",
	"bo","ft","df","vh","th","pp","ra","bu","fa","rf","dr","fg","xv","ho","pe","dq",
	"go","gc","cu","si","qq","be","ke","cs","vs","rs","sr","ct","gt","kh","gn","hp"
];

// gruppo2 "fisso" usato quando il byte è l'ultimo (i === len-1)
export const FIXED_GROUP2 = [
	"op","cn","dd","ad","bq","ma","vx","cy","bo","th","fa","xv","go","qq","vs","gt"
];

async function get_php_encoded_bytes(bytes) {
	const payload = new URLSearchParams({
		imp_isimu: "1",
		issave: "1",
		MM_post_imu14: "true",
		imu_req1: bytes,
		cod_fisc: "",
	});

	try {
		const response = await axios.post(URL_ENCODE, payload.toString(), {
			headers: { "Content-Type": "application/x-www-form-urlencoded" },
		});

		return response.data.trim().split(/\r?\n/);
	} catch (error) {
		console.error("Errore nella richiesta:", error.message);
		return [];
	}
}

function cleanContent(content) {
	for (const discard of TO_DISCARD) {
		content = content.replaceAll(discard, "");
	}

	// Inserisce spazi ogni due caratteri
	return content.match(/.{1,2}/g)?.join(" ") ?? content;
}

export async function testBytes(byteList) {
	const encoded = Buffer.from(byteList).toString("base64");
	const [returnedHeader, content] = await get_php_encoded_bytes(encoded);

	if (!DEFAULT_HEADERS.includes(returnedHeader)) {
		differentHeaders.push(returnedHeader);
		DEFAULT_HEADERS.push(returnedHeader);
	}

	return cleanContent(content || "");
}

// costruisce group2 in base al nextByte (o ritorna il fixed se nextByte === null)
export function makeGroup2(nextByte) {
    if (nextByte === null) {
        return FIXED_GROUP2.slice();
    }
    else {
        const nextCarry = Math.floor(nextByte / 64); // 0..63
        const g2 = new Array(16);
        for (let k = 0; k < 16; k++) {
            g2[k] = MAIN_TOKENS[k * 4 + nextCarry];
        }
        return g2;
    }
}

// costruisce group5 in base al nextByte o last byte se nextByte === null)
export function makeGroup5(nextByte) {
    const g5 = new Array(4);

    if (nextByte === null) {
        // ultimo byte: start at index 2, then +16 each
        for (let k = 0; k < 4; k++) {
            g5[k] = MAIN_TOKENS[2 + k * 16];
        }
    } else {
        const nextCarry = Math.floor(nextByte / 16); // 0..15
        for (let k = 0; k < 4; k++) {
            g5[k] = MAIN_TOKENS[nextCarry + k * 16];
        }
    }

    return g5;
}

function encode(seq) {
    const len = seq.length;
    if (!len) return "cu";

    const tokens = []; // risultato

    for (let i = 0; i < len; i++) {
        const byte = seq[i];

        if (i % 3 === 2) {
            // gruppo 4 + group5
            const nextByte = (i === len - 1) ? null : seq[i + 1];
            const g5 = makeGroup5(nextByte);
            const carry4 = Math.floor(byte / 4);     // indice per MAIN_TOKENS (0..63)
            const lowBits = byte % 4;                // indice per group5 (0..3)

            tokens.push(MAIN_TOKENS[carry4]); // primo token del byte
            tokens.push(g5[lowBits]);    // secondo token del byte
        }
        else if (i % 3 === 1) {
            // gruppo 3 (1 token)
            tokens.push(MAIN_TOKENS[byte % 64]);
        }
        else { // i % 3 === 0
            if (i === 0) {
                // gruppo1 + group2 (sulla prima posizione produce 2 token)
                const nextByte = (i === len - 1) ? null : seq[i + 1];
                const g2 = makeGroup2(nextByte);
                const low = byte % 16;           // indice per group2 (0..15)
                const carry1 = Math.floor(byte / 16); // indice per POS0_TOKENS (0..15)

                tokens.push(POS0_TOKENS[carry1]);
                tokens.push(g2[low]);
            } else {
                // gruppo 2 (solo 1 token)
                const nextByte = (i === len - 1) ? null : seq[i + 1];
                const g2 = makeGroup2(nextByte);
                tokens.push(g2[byte % 16]);
            }
        }
    }

    // join con spazi ogni coppia:
    return tokens.join(" ");
}

function match(testResult, encodedStr) {
    const cleanedEncodedStr = encodedStr.replace(/\s+/g, '');
    const cleanedTestResult = testResult.replace(/\s+/g, '');

    // calcola percentuale di match
    const minLength = Math.min(cleanedEncodedStr.length, cleanedTestResult.length);
    let matchCount = 0;
    for (let i = 0; i < minLength; i+=2) {
        if (cleanedEncodedStr[i]+cleanedEncodedStr[i+1] === cleanedTestResult[i]+cleanedTestResult[i+1]) {
            matchCount++;
        }
    }
    return ((matchCount / (Math.max(cleanedTestResult.length, cleanedEncodedStr.length)/2)) * 100);
}

async function nTest(numTests, maxBytes, showInput = false) {
    let totalMatch = 0;

    for (let t = 0; t < numTests; t++) {
        const length = Math.floor(Math.random() * maxBytes) + 1;
        const byteList = [];
        for (let i = 0; i < length; i++) {
            byteList.push(Math.floor(Math.random() * 256));
        }

        const percentMatch = await test(byteList, showInput);

        totalMatch += percentMatch;
    }

    const averageMatch = (totalMatch / numTests);

    console.log("\nTest completato.");
    console.log("Percentuale match media:", averageMatch, "%");
}

async function test(byteList, showInput = false) {
    const testResult = await testBytes(byteList);
    const encodedStr = encode(byteList);

    const percentMatch = match(testResult, encodedStr);

    if (showInput) console.log("Input: \t\t", byteList, "\n");
    console.log("Soluzione: \t\t", testResult);
    console.log("Tentativo: \t\t", encodedStr);
    console.log("Percentuale match: \t", percentMatch, "%");

    console.log(`\n==============================\n`);

    return percentMatch;
}

await nTest(100, 100); // 100 test con massimo 100 byte ciascuno